/**************************************************************************************************
**                                                                                               **
**  文件名称:  osl_mem_man.c                                                                     **
**  版权所有:  CopyRight @ Xiamen Yaxon NetWork CO.LTD. 2017                                     **
**  文件描述:  系统动态内存管理模块                                                              **
**  ===========================================================================================  **
**  创建信息:  | 2017-6-29 | LEON | 创建本模块                                                   **
**  ===========================================================================================  **
**  修改信息:  单击此处添加....                                                                  **
**************************************************************************************************/
#include "osl_include.h"
#include "osl_mem_man.h"

/*************************************************************************************************/
/*                           定义模块配置参数                                                    */
/*************************************************************************************************/
#define MEM_MAGFLAG          0x55                                              /* 状态标记 */
#define MEM_INFRMNO          3                                                 /* 通知函数个数 */
#define MEM_MAXSIZE         (4 * 1024)                                         /* 内存池大小 */

/*************************************************************************************************/
/*                           定义模块数据结构                                                    */
/*************************************************************************************************/
typedef struct blockhead_t {
    struct blockhead_t  *prev;                                                 /* 链表指针 */
    struct blockhead_t  *next;                                                 /* 链表指针 */
    INT8U                flag;                                                 /* 固定为MEM_MAGFLAG */
    INT8U                used;                                                 /* 是否已分配：0-N，1-Y */ 
    INT16U               size;                                                 /* 区域大小 */
} MEM_BLKHEAD_T;

/*************************************************************************************************/
/*                           定义模块变量                                                        */
/*************************************************************************************************/
static INT32U        s_mempools[(MEM_MAXSIZE + 3) / 4];
static HMEM_STAT_T   s_memstatus;
static MEM_INFORM_T  s_meminform[MEM_INFRMNO];

/**************************************************************************************************
**  函数名称:  InformMemState
**  功能描述:  执行内存变动的通知函数
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
static void InformMemState(BOOLEAN alloc, void *mptr, INT32U mlen, char *file, INT32U line, HMEM_STAT_T *stat)
{
    INT8U  i, ppos;
    char  *pptr;

    for (pptr = file, ppos = YX_STRLEN(file); ppos > 0; ppos--) {              /* 提取有效的文件名 */
        if (pptr[ppos - 1] == '\\') {
            break;
        }
    }
    
    pptr += ppos;

    if (alloc == TRUE) {                                                       /* 回调申请内存的通知函数 */
        for (i = 0; i < MEM_INFRMNO; i++) {
            if (s_meminform[i].a_inform != NULL) {
                s_meminform[i].a_inform(mptr, mlen, pptr, line, stat);
            }
        }
    } else {                                                                   /* 回调释放内存的通知函数 */
        for (i = 0; i < MEM_INFRMNO; i++) {
            if (s_meminform[i].f_inform != NULL) {
                s_meminform[i].f_inform(mptr, mlen, pptr, line, stat);
            }
        }
    }
}

/**************************************************************************************************
**  函数名称:  OSL_MemManInit
**  功能描述:  初始化堆内存管理模块
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void OSL_MemManInit(void)
{
    INT8U           i;
    MEM_BLKHEAD_T  *bptr;
    
    bptr       = (MEM_BLKHEAD_T *)s_mempools;                                  /* 获取整块内存池的起始地址 */
    bptr->next = 0;
    bptr->prev = 0;
    bptr->used = FALSE;
    bptr->flag = MEM_MAGFLAG;
    bptr->size = sizeof(s_mempools) - sizeof(MEM_BLKHEAD_T);                   /* 计算内存池中可用的容量大小 */
    
    s_memstatus.blocks++;                                                      /* 初始化内存块数量 */
    s_memstatus.ubytes = 0;
    s_memstatus.lbytes = MEM_MAXSIZE;
    
    for (i = 0; i < MEM_INFRMNO; i++) {                                        /* 初始化各个通知函数为空 */
        s_meminform[i].a_inform = NULL;
        s_meminform[i].f_inform = NULL;
    }
}

/**************************************************************************************************
**  函数名称:  OSL_MemManAlloc
**  功能描述:  申请分配内存接口
**  输入参数:  dlen:  申请内存的长度
**             file:  申请内存的文件名
**             line:  申请内存的行号
**             trac:  是否要跟踪回调本次操作的结果
**  输出参数:  无
**  返回参数:  成功返回内存指针，失败返回0
**************************************************************************************************/
void *OSL_MemManAlloc(INT32U dlen, char *file, INT32U line, BOOLEAN trac)
{
    void           *aptr;
    MEM_BLKHEAD_T  *bptr;                                                      /* 指向内存池的起始位置 */
    MEM_BLKHEAD_T  *newblock;                                                  /* 本次操作完后产生的新块(即剩余的内存块) */
    
    if (dlen == 0 || dlen >= MEM_MAXSIZE) {
        return 0;
    }
    
    bptr = (MEM_BLKHEAD_T *)s_mempools;
    
    dlen = ((dlen + 3) / 4) * 4;
    
    while (bptr != 0) {
        
        if (bptr->used == FALSE && bptr->size >= dlen) {
            bptr->used = MEM_MAGFLAG;
            
            if ((bptr->size - dlen) > sizeof(MEM_BLKHEAD_T)) {
                
                newblock       = (MEM_BLKHEAD_T *)(((INT8U *)bptr) + sizeof(MEM_BLKHEAD_T) + dlen); /* 扣掉帧头偏移量，再扣掉本次需要分配的量 */
                newblock->flag = MEM_MAGFLAG;
                newblock->size = bptr->size - dlen - sizeof(MEM_BLKHEAD_T);    /* 剩下的内存块的容量 */
                newblock->used = FALSE;
                newblock->prev = bptr;                                         /* 将新块链接到本次申请的这个块后面 */
                newblock->next = bptr->next;                                   /* 将本次申请的块插入到剩下的块之前 */

                if (bptr->next != 0) {
                    bptr->next->prev = newblock;
                }

                bptr->next = newblock;                                         /* 将本次申请的块的下一指针指向剩余的内存块 */
                bptr->size = dlen;                                             /* 指定本次申请的内存块大小 */
                s_memstatus.blocks++;                                          /* 所分配的内存块数累加 */
            }
            
            s_memstatus.ubytes += bptr->size;                                  /* 将本次所申请掉的内存容量计入已经使用的范围 */
            s_memstatus.lbytes -= bptr->size;
            break;
        }
        
        bptr = bptr->next;                                                     /* 否则，本块不满足分配要求，判断下一块 */
    }
    
    aptr = (bptr != 0) ? ((void *)(((INT8U *)(bptr)) + sizeof(MEM_BLKHEAD_T))) : NULL;   /* 转换成所申请的内存块的有效地址值 */
    
    if (trac == TRUE) {                                                        /* 需要跟踪回调本次操作的结果 */
        InformMemState(TRUE, aptr, dlen, file, line, &s_memstatus);
    }
    
    return aptr;
}

/**************************************************************************************************
**  函数名称:  OSL_MemManFree
**  功能描述:  释放内存接口
**  输入参数:  sptr:  释放内存的地址
**             file:  申请内存的文件名
**             line:  申请内存的行号
**             trac:  是否要跟踪回调本次操作的结果
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void OSL_MemManFree(void *sptr, char *file, INT32U line, BOOLEAN trac)
{
    INT16U          flen;
    MEM_BLKHEAD_T  *bptr = 0;
    MEM_BLKHEAD_T  *prev = 0;
    MEM_BLKHEAD_T  *back = 0;
    MEM_BLKHEAD_T  *next = 0;
    
    SYS_ASSERT((sptr != 0), RETURN_VOID);
    bptr = (MEM_BLKHEAD_T *)(((INT8U *)(sptr)) - sizeof(MEM_BLKHEAD_T));       /* 将指针指向所需要释放的内存块的起始地址 */
    
    SYS_ASSERT((bptr->flag == MEM_MAGFLAG), RETURN_VOID);
    SYS_ASSERT((bptr->used == MEM_MAGFLAG), RETURN_VOID);
    
    bptr->used = FALSE;
    s_memstatus.ubytes -= bptr->size;                                          /* 从已使用的内存值总量中扣去本次释放的值 */
    s_memstatus.lbytes += bptr->size;
    
    flen = bptr->size;                                                         /* 记录本次释放的内存大小 */
    
    prev = bptr->prev;                                                         /* 获取前块指针的起始地址 */
    back = bptr->next;                                                         /* 获取后块指针的起始地址 */
    
    if (bptr->prev != 0) {                                                     /* 如果前面有其他内存块，进行前项合并 */
        if (prev->used == FALSE) {
            prev->size = prev->size + bptr->size + sizeof(MEM_BLKHEAD_T);      /* 将本次释放的块大小归并到前块中 */
            prev->next = bptr->next;                                           /* 将后块与前块串接起来 */
            if (back != 0) {
                back->prev = prev;                                             /* 将前块与后块串接起来 */
            }
            s_memstatus.blocks--;                                              /* 将所分配的内存块数累减 */
        }
    }
    
    if (bptr->next != 0) {                                                     /* 如果合并完后的内存块后面有还有其他内存块，再进行后项合并 */
        if (prev != 0) {
            if (prev->next == bptr->next) {
                bptr = prev;                                                   /* 获取前面内存块的起始地址 */
            }
        }
        back = bptr->next;                                                     /* 获取后面内存块的起始地址 */
        next = back->next;                                                     /* 获取再下一个内存块的起始地址 */
        if (back->used == FALSE) {
            bptr->size = bptr->size + back->size + sizeof(MEM_BLKHEAD_T);      /* 将两个内存块的大小合并 */
            bptr->next = back->next;                                           /* 指定合并后的内存块的下一指针 */
            if (next != 0) {
                next->prev = bptr;                                             /* 将本块内存与原本的再下一个内存块链接起来 */
            }
            s_memstatus.blocks--;                                              /* 将所分配的内存块数累减 */
        }    
    }
    
    if (s_memstatus.ubytes == 0 && s_memstatus.blocks != 1) {
        SYS_ASSERT((0), RETURN_VOID);
    }
    
    if (trac == TRUE) {                                                        /* 需要跟踪回调本次操作的结果 */
        InformMemState(FALSE, sptr, flen, file, line, &s_memstatus);
    }
}

/**************************************************************************************************
**  函数名称:  OSL_MemManRegA
**  功能描述:  注册内存申请结果的通知函数 【每次系统存在内存申请的动作时，自动回调该函数】
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void OSL_MemManRegA(void (*a_inform)(void *, INT32U, char *, INT32U, HMEM_STAT_T *))
{
    INT8U i;
    
    for (i = 0; i < MEM_INFRMNO; i++) {                                        /* 寻找空闲的通知函数 */
        if (s_meminform[i].a_inform == NULL) {
            break;
        }
    }
    
    SYS_ASSERT(i < MEM_INFRMNO, RETURN_VOID);
    
    s_meminform[i].a_inform = a_inform;
}

/**************************************************************************************************
**  函数名称:  OSL_MemManRegF
**  功能描述:  注册内存释放结果的通知函数 【每次系统存在内存释放的动作时，自动回调该函数】
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void OSL_MemManRegF(void (*f_inform)(void *, INT32U, char *, INT32U, HMEM_STAT_T *))
{
    INT8U i;
    
    for (i = 0; i < MEM_INFRMNO; i++) {                                        /* 寻找空闲的通知函数 */
        if (s_meminform[i].f_inform == NULL) {
            break;
        }
    }
    
    SYS_ASSERT(i < MEM_INFRMNO, RETURN_VOID);
    
    s_meminform[i].f_inform = f_inform;
}

/**************************************************************************************************
**  函数名称:  OSL_MemManGetStat
**  功能描述:  查询内存池当前使用状态接口
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
HMEM_STAT_T *OSL_MemManGetStat(void)
{
    return &s_memstatus;
}


